package pl;

import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.JCSystem;
import javacard.framework.Util;
import javacard.security.MessageDigest;

public class HMAC {

	private final static short BLOCKSIZE = (short) 64;

	byte[] hmacValue;
	short hmacLen;
	
	private byte[] k_xor_ipad;
	private byte[] k_xor_opad;
	
	private MessageDigest hmac;

	public HMAC() {

		hmacValue = JCSystem.makeTransientByteArray(BLOCKSIZE,
				JCSystem.CLEAR_ON_DESELECT);
		
		k_xor_ipad = new byte[BLOCKSIZE]; 
		k_xor_opad = new byte[BLOCKSIZE]; 
		
	}
	
	public void init(MessageDigest mdInstance, byte[] key, short kLen) {

		hmac = mdInstance;
		hmacLen = hmac.getLength();

		Utils.ClearArray(k_xor_ipad);
		Utils.ClearArray(k_xor_opad);

		if (hmac == null) ISOException.throwIt((short) (ISO7816.SW_WRONG_LENGTH + 4));
		
		if (kLen > BLOCKSIZE) {
			
			hmac.reset();
			hmac.doFinal(key, (short) 0, kLen, k_xor_ipad, (short) 0);
			
			for (short i = hmacLen; i < BLOCKSIZE; i++)
            {
				k_xor_ipad[i] = (byte)0;
            }
			
			kLen = hmacLen;	
		}
		else
		{
			Util.arrayCopy(key, (short) 0, k_xor_ipad, (short) 0, kLen);
		}

		Util.arrayCopy(k_xor_ipad, (short) 0, k_xor_opad, (short) 0, kLen);

		for (short i = 0; i < BLOCKSIZE; i++) {
			k_xor_ipad[i] ^= 0x36;
			k_xor_opad[i] ^= 0x5C;
		}

		hmac.update(k_xor_ipad, (short) 0, BLOCKSIZE);
		
	}

	public void init(byte[] key, short kLen)
	{
		hmac.reset();
		
		Utils.ClearArray(k_xor_ipad);
		Utils.ClearArray(k_xor_opad);

		if (kLen > BLOCKSIZE) {
			// error
			hmac.reset();
			hmac.doFinal(key, (short) 0, kLen, k_xor_ipad, (short) 0);
			
			for (short i = hmacLen; i < BLOCKSIZE; i++)
            {
				k_xor_ipad[i] = (byte)0;
            }
			
			kLen = hmacLen;	
		}
		else
		{
			Util.arrayCopy(key, (short) 0, k_xor_ipad, (short) 0, kLen);
		}

		Util.arrayCopy(k_xor_ipad, (short) 0, k_xor_opad, (short) 0, kLen);

		for (short i = 0; i < BLOCKSIZE; i++) {
			k_xor_ipad[i] ^= 0x36;
			k_xor_opad[i] ^= 0x5C;
		}

		hmac.update(k_xor_ipad, (short) 0, BLOCKSIZE);
	}
	
	public short getDigestLength() {
		return hmacLen;
	}

	public void update(byte[] message, short off, short mLen) {
		hmac.update(message, off, (short) mLen);
	}

	public void reset() {
		hmac.reset();
		hmac.update(k_xor_ipad, (short) 0, (short) k_xor_ipad.length);
	}

	public void doFinal(byte[] out, short off) {
		hmac.doFinal(new byte[0], (short) 0, (short) 0, hmacValue, (short) 0);
		hmac.update(k_xor_opad, (short) 0, (short) k_xor_opad.length);
		hmac.doFinal(hmacValue, (short) 0, (short) hmacLen, out, (short) 0);
		hmac.update(k_xor_ipad, (short) 0, (short) k_xor_ipad.length);
		hmac.reset();
	}
}
